package com.jsbs.iam.ident.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jsbs.iam.common.core.dto.Result;
import com.jsbs.iam.common.core.utils.Json;
import com.jsbs.iam.common.core.utils.PageHelperTool;
import com.jsbs.iam.common.redis.service.RedisUtils;
import com.jsbs.iam.ident.constant.RedisConstants;
import com.jsbs.iam.ident.dto.IamIdentApplySystemDto;
import com.jsbs.iam.ident.dto.IamMultiDeviceStrategyPageDto;
import com.jsbs.iam.ident.entity.IamMultideviceStrategy;
import com.jsbs.iam.ident.feign.IdentFeignInter;
import com.jsbs.iam.ident.mapper.IamMultideviceStrategyMapper;
import com.jsbs.iam.ident.service.IamMultiDeviceStrategyService;
import com.jsbs.iam.ident.vo.IamMultideviceStrategyVo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author zhouxiangyang
 * @className MultiDeviceStrategyServiceImpl
 * @date 2023/1/2 22:06
 * @description:
 */
@Service
public class MultiDeviceStrategyServiceImpl implements IamMultiDeviceStrategyService {

    private static final Logger LOGGER = LoggerFactory.getLogger(MultiDeviceStrategyServiceImpl.class);

    @Autowired
    private IamMultideviceStrategyMapper iamMultideviceStrategyMapper;

    @Autowired
    private IdentFeignInter identFeignInter;

    @Resource
    private RedissonClient redissonClient;

    @Value("${_env.name}")
    private String envName;

    @Override
    public PageInfo<IamMultideviceStrategyVo> getIamMultiDeviceStrategyPage(IamMultiDeviceStrategyPageDto pageDto,
                                                                            String companyCode) {

        //初始化分页
        pageDto.init();
        PageHelper.startPage(pageDto.getCurrPage(),pageDto.getPageSize());
        //查询所有多设备登录策略的数据
        List<IamMultideviceStrategy> iamMultiDeviceStrategyAll =
                iamMultideviceStrategyMapper.getIamMultiDeviceStrategyAll(pageDto.getStrategyNameOrCode(),companyCode);
        PageInfo<IamMultideviceStrategy> page = new PageInfo<>(iamMultiDeviceStrategyAll);

        iamMultiDeviceStrategyAll.forEach(IamMultideviceStrategy ->{
            String applyTypeCode = IamMultideviceStrategy.getApplyTypeCode();
            String applyTypeName = getApplyTypeName(applyTypeCode);
            IamMultideviceStrategy.setApplyTypeCode(applyTypeName);
        });
        //entity转vo
        List<IamMultideviceStrategyVo> IamMultideviceStrategyVos =
                convertMultideviceStrategyVo(iamMultiDeviceStrategyAll);

        PageInfo<IamMultideviceStrategyVo> pageInfo = new PageInfo<>(IamMultideviceStrategyVos);
        int total = (int) page.getTotal();
        PageInfo<IamMultideviceStrategyVo> multiDevicePageInfo = PageHelperTool.initPageInfoObj(
                    pageDto.getCurrPage(), total, pageDto.getPageSize(), pageInfo);

        return multiDevicePageInfo;
    }

    @Override
    public int saveIamMultiDeviceStrategy(IamMultideviceStrategy iamMultideviceStrategy) throws Exception {

        if (iamMultideviceStrategy == null) {
            throw new Exception("新增多设备登录策略时，入参不能为空");
        }
        if (StringUtils.isEmpty(iamMultideviceStrategy.getApplyTypeCode())) {
            throw new Exception("应用系统不能为空");
        }
        if (StringUtils.isEmpty(iamMultideviceStrategy.getMultideviceStrategyName())){
            throw new Exception("多设备登陆策略名称不能为空");
        }

        // 新增异常登录策略时，策略编码加锁，失效时间10秒钟
        RLock lock = redissonClient.getLock(RedisConstants.MULTI_DEVICE_CODE_LOCK);
        Long code = null;
        try {
            // 5分钟内重试获取锁，10秒后释放
            boolean isLocked = lock.tryLock(5 * 60 * 1000L, 10 * 1000L, TimeUnit.MILLISECONDS);
            // 获取到锁后，进行自增
            if (!isLocked) {
                LOGGER.warn("Route=>MultiDeviceStrategyServiceImpl method=>saveIamMultiDeviceStrategy 新增多设备登陆策略时策略编码生成失败:{}",
                        Json.toJsonString(iamMultideviceStrategy));
                throw new Exception();
            }
            // 加锁成功后获取当前缓存值
            String value = RedisUtils.get(envName + RedisConstants.MULTI_DEVICE_LOGIN_CODE);
            // 若为空则从数据库取当前最大值，并加新增用户数量入缓存
            if (StringUtils.isEmpty(value)) {
                code = Long.valueOf(iamMultideviceStrategyMapper.getIamMultiDeviceStrategyCode()) + 1;
            } else {
                // 当前缓存获取成功，并加新增用户数量入缓存
                code = Long.valueOf(value) + 1;
            }
            // 将最新值更新到缓存中
            RedisUtils.set(envName + RedisConstants.MULTI_DEVICE_LOGIN_CODE, String.valueOf(code));
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("Route=>MultiDeviceStrategyServiceImpl method=>saveIamMultiDeviceStrategy 新增多设备登陆策略时策略编码生成失败:{}",
                    Json.toJsonString(iamMultideviceStrategy));
            throw new RuntimeException("新增多设备登录策略异常");
        } finally {
            // 手动解锁
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
        AtomicReference<Long> finalCode = new AtomicReference<>(code);
        String applyTypeName = iamMultideviceStrategy.getApplyTypeCode();
        String applyTypeCode = getApplyTypeCode(applyTypeName);
        iamMultideviceStrategy.setApplyTypeCode(applyTypeCode);
        MultideviceStrategyNameVerification(iamMultideviceStrategy);
        iamMultideviceStrategy.setCreateTime( new Date());
        iamMultideviceStrategy.setMultideviceStrategyCode("CN" + String.format("%04d", finalCode.getAndSet(finalCode.get())));
        //新增异常登录策略
        int i = iamMultideviceStrategyMapper.insertSelective(iamMultideviceStrategy);
        return i;
    }

    @Override
    public int updateIamMultiDeviceStrategy(IamMultideviceStrategy iamMultideviceStrategy) throws Exception {
        String applyName = iamMultideviceStrategy.getApplyTypeCode();
        if (StringUtils.isNotEmpty(applyName)) {
            //根据应用系统名称获取应用系统编码
            String applyTypeCode = getApplyTypeCode(applyName);
            iamMultideviceStrategy.setApplyTypeCode(applyTypeCode);
        }
        //校验策略名称和应用编码是否重复
        MultideviceStrategyNameVerification(iamMultideviceStrategy);
        iamMultideviceStrategy.setUpdateTime(new Date());
        int i = iamMultideviceStrategyMapper.updateByPrimaryKey(iamMultideviceStrategy);
        return i;
    }

    @Override
    public int deleteIamMultiDeviceStrategy(List<String> list,String companyCode) {
        int i = iamMultideviceStrategyMapper.deleteByPrimaryKey(list,companyCode);
        return i;
    }

    private void MultideviceStrategyNameVerification (IamMultideviceStrategy iamMultideviceStrategy) throws Exception {
        String multideviceStrategyName = iamMultideviceStrategy.getMultideviceStrategyName();
        if (StringUtils.isNotEmpty(multideviceStrategyName)) {
            IamMultideviceStrategy multiDeviceSStrategy =
                    iamMultideviceStrategyMapper.getMultiDeviceSStrategyName(multideviceStrategyName);

            if (multiDeviceSStrategy != null) {
                String codeDatabase = multiDeviceSStrategy.getMultideviceStrategyCode();
                String code = iamMultideviceStrategy.getMultideviceStrategyCode();
                //数据库查询编码和入参编码如果一致，说明名称重复
                if (!codeDatabase.equals(code)) {
                    throw new Exception("多设备登录策略名称重复，请更重新命名异常登录策略名称");
                }
            }
        }

        //应用系统编码
        String applyTypeCode = iamMultideviceStrategy.getApplyTypeCode();
        if (StringUtils.isNotEmpty(applyTypeCode)) {
            //根据应用系统编码查询策略
            IamMultideviceStrategy multiDeviceStrategy =
                    iamMultideviceStrategyMapper.getApplyTypeCode(applyTypeCode);
            if (multiDeviceStrategy != null ){
                String codeDatabase = multiDeviceStrategy.getMultideviceStrategyCode();
                String code = iamMultideviceStrategy.getMultideviceStrategyCode();
                //如果入参的策略编码和查询到的策略编码不一致则策略名称重复，进行异常抛出
                if (!codeDatabase.equals(code)) {
                    throw new Exception("该应用已经绑定了多设备登录策略");
                }
            }
        }

    }

    private String getApplyTypeCode (String applyName)  {
        IamIdentApplySystemDto iamApplySystemDto = new IamIdentApplySystemDto();
        iamApplySystemDto.setApplyName(applyName);
        //先查应用系统编码
        Result applyTypeCode = identFeignInter.getApplyTypeCode(iamApplySystemDto);
        Object data = applyTypeCode.getData();
        if (data != null) {

        }
        return  applyTypeCode.getData().toString();
    }

    private String getApplyTypeName (String applyTypeCode)  {
        IamIdentApplySystemDto identApplySystemDto = new IamIdentApplySystemDto();
        identApplySystemDto.setApplyName(applyTypeCode);
        Result applyTypeName = identFeignInter.getApplyTypeName(identApplySystemDto);
        Object data = applyTypeName.getData();
        if (data != null) {
            return  data.toString();
        } else {
            LOGGER.info("应用类型没有对应的名称数据，使用应用类型返回{}",applyTypeCode);
            return applyTypeCode;
        }
    }

    private List<IamMultideviceStrategyVo> convertMultideviceStrategyVo (List<IamMultideviceStrategy> iamMultiDeviceStrategyAll) {
        List<IamMultideviceStrategyVo> list = new ArrayList<>();
        iamMultiDeviceStrategyAll.forEach(IamMultideviceStrategy ->{
            IamMultideviceStrategyVo multiDeviceStrategyVo = new IamMultideviceStrategyVo();
            BeanUtils.copyProperties(IamMultideviceStrategy,multiDeviceStrategyVo);
            Date createTime = IamMultideviceStrategy.getCreateTime();
            //创建时间转换成String
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String strDate = sdf.format(createTime);
            multiDeviceStrategyVo.setCreateTime(strDate);
            list.add(multiDeviceStrategyVo);
        });
        return list;
    }
}
